import { BufferAttribute, BufferGeometry, Color, Points, PointsMaterial, ShaderMaterial } from 'three'
import vt from './vt.vert?raw'
import fs from './fs.frag?raw'

class ParticleEffect {
  pointNumber: number
  bufferGeometry: BufferGeometry
  position: Float32Array
  particle: Points<BufferGeometry, ShaderMaterial>
  toPosition: Float32Array
  shaderMaterial: ShaderMaterial

  constructor(pointNumber = 1000) {
    this.pointNumber = pointNumber

    this.bufferGeometry = new BufferGeometry()
    this.position = new Float32Array(this.pointNumber * 3)
    this.toPosition = new Float32Array(this.pointNumber * 3)

    this.bufferGeometry.setAttribute('position', new BufferAttribute(this.position, 3))
    this.bufferGeometry.setAttribute('toPosition', new BufferAttribute(this.toPosition, 3))

    this.shaderMaterial = new ShaderMaterial({
      vertexShader: vt,
      fragmentShader: fs,
      uniforms: {
        progress: { value: 0 },
      },
    })

    this.particle = new Points(this.bufferGeometry, this.shaderMaterial)

    this.init()
  }

  init() {
    const { random } = Math
    for (let i = 0; i < this.pointNumber; i++) {
      const i3 = i * 3
      this.toPosition[i3] = (0.5 - random()) * 10
      this.toPosition[i3 + 1] = (0.5 - random()) * 10
      this.toPosition[i3 + 2] = (0.5 - random()) * 10
    }
    this.bufferGeometry.attributes.position.needsUpdate = true
  }

  to(geometry: BufferGeometry) {
    const { array, count } = geometry.getAttribute('position') as BufferAttribute

    let targetIndex = 0

    for (let i = 0; i < this.pointNumber; i++) {
      const i3 = i * 3

      targetIndex %= count
      const targetIndex3 = targetIndex * 3

      this.position[i3] = this.toPosition[i3]
      this.position[i3 + 1] = this.toPosition[i3 + 1]
      this.position[i3 + 2] = this.toPosition[i3 + 2]

      this.toPosition[i3] = array[targetIndex3]
      this.toPosition[i3 + 1] = array[targetIndex3 + 1]
      this.toPosition[i3 + 2] = array[targetIndex3 + 2]

      targetIndex++
    }
    this.bufferGeometry.attributes.position.needsUpdate = true
    this.bufferGeometry.attributes.toPosition.needsUpdate = true
  }

  setProgress(progress: number) {
    this.shaderMaterial.uniforms.progress.value = progress
  }
}

export default ParticleEffect
